package ar.com.jsejtman.common.security.credential.service;

import ar.com.jsejtman.common.mail.entity.EmailServerConfiguration;
import ar.com.jsejtman.common.mail.exception.CannotSendEmailException;
import ar.com.jsejtman.common.mail.service.IEmailSenderService;
import ar.com.jsejtman.common.mail.service.IEmailServerConfigurationService;
import ar.com.jsejtman.common.persistence.exception.DaoException;
import ar.com.jsejtman.common.security.credential.entity.PasswordResetEmailContent;
import ar.com.jsejtman.common.security.credential.entity.SecurityCredential;
import ar.com.jsejtman.common.security.credential.exception.InvalidUsernameException;
import ar.com.jsejtman.common.security.credential.exception.UsernameNotMatchingEmailAddressException;
import ar.com.jsejtman.common.security.credential.util.PasswordUtils;
import ar.com.jsejtman.common.security.schema.dao.SecurityUserDao;
import ar.com.jsejtman.common.security.schema.entity.SecurityUser;
import ar.com.jsejtman.common.service.definition.BaseService;
import ar.com.jsejtman.common.service.exception.EntityRetrievalException;
import ar.com.jsejtman.common.service.exception.EntitySavingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Implementación personalizada de UserDetailsService (Spring Security) para Conexia. Permite la
 * obtención de información del usuario, así como sus respectivos permisos de seguridad
 * (authorities), de la base de datos del sistema.
 * 
 * @author jsejtman
 */
@Service
@Transactional
public class SecurityCredentialService extends BaseService implements UserDetailsService,
        ISecurityCredentialService {

	/**
	 * Servicio para obtener la configuración de los servidores de Emails.
	 */
	@Autowired
	private IEmailServerConfigurationService emailServiceConfigurationService;

	/**
	 * Servicio para realizar el envío de Emails.
	 */
	@Autowired
	private IEmailSenderService emailSenderService;

	/**
	 * Servicio para obtener el contenido del Email de reinicio de clave.
	 */
	@Autowired
	private IPasswordResetEmailContentService passwordResetEmailContentService;

	@Override
	public final UserDetails loadUserByUsername(final String username) {

		try {
			final SecurityUserDao dao = this.getDaoFactory().newDao(SecurityUserDao.class);

			final SecurityUser securityUserFound = dao.getSecurityUserByUsername(username);

			if (securityUserFound == null) {
				throw new UsernameNotFoundException(username);
			}

			return (new SecurityCredential(securityUserFound));

		} catch (final DaoException e) {
			throw new DataRetrievalFailureException(e.getMessage(), e);
		}
	}

	@Override
	public final void resetUserPassword(final String username, final String emailAddress)
	        throws InvalidUsernameException, UsernameNotMatchingEmailAddressException,
	        EntityRetrievalException, EntitySavingException, CannotSendEmailException {

		if (username == null || emailAddress == null) {
			throw new IllegalArgumentException(
			        "Al menos un parametro requerido tiene un valor nulo.");
		}

		SecurityUserDao dao = null;

		SecurityUser securityUserFound = null;

		try {
			dao = this.getDaoFactory().newDao(SecurityUserDao.class);

			securityUserFound = dao.getSecurityUserByUsername(username);

			if (securityUserFound == null) {
				throw new InvalidUsernameException();
			} else if (!emailAddress.equals(securityUserFound.getEmailAddress())) {
				throw new UsernameNotMatchingEmailAddressException();
			}

		} catch (final DaoException e) {
			throw new EntityRetrievalException(e.getMessage(), e);
		}

		final String generatedPassword = PasswordUtils.generateRandomPassword();

		final String encryptedPassword = PasswordUtils.encryptPassword(generatedPassword);

		try {
			securityUserFound.setPassword(encryptedPassword);

			dao.saveOrUpdate(securityUserFound);
		} catch (final DaoException e) {
			throw new EntitySavingException(e.getMessage(), e);
		}

		try {
			/* Obtiene la configuración para el envío de Emails */
			final EmailServerConfiguration serverConfiguration =
			        this.emailServiceConfigurationService.getEmailServerConfiguration();

			final PasswordResetEmailContent content =
			        this.passwordResetEmailContentService.getContent();

			final String finalBody = content.getBody().replaceAll("--password--", generatedPassword);

			this.emailSenderService.sendEmail(serverConfiguration,
			        securityUserFound.getEmailAddress(), content.getSubject(), finalBody);

		} catch (final Exception e) {
			throw new CannotSendEmailException(e.getMessage());
		}
	}
}
